昨天看完useContext,今天要來看讓我非常興奮的useReducer
!之所以興奮是因為,之前第一次看到redux時,覺得超複雜的,什麼reducer、store、dispatch...怎麼看不太懂。今天的部分你只要學完了,未來要使用redux時,應該會覺得「咦,怎麼這些語法這麼熟悉?」!讓我們開始吧:
讓我們來看看useReducer在型別宣告檔內的說明吧,你應該會更清楚什麼時候要用它:
useReducer
is usually preferable touseState
when you have complex state logic that involves multiple sub-values. It also lets you optimize performance for components that trigger deep updates because you can passdispatch
down instead of callbacks.
簡單來說,當你有複雜的狀態需要管理時,就可以使用useReducer來管理,他甚至能為你帶來效能上的改善,因為平常使用useState時,我們都會將callback傳入子層來改變state,這點有可能造成不必要的渲染。而使用useReducer後,我們只要將dispatch這方法傳下去子層就好。
要使用useReducer,我們需要一些起手式:
//覺得reducer的相關用詞怎麼翻都不太理想
我們還是用超超超基本的計數器來當作範例的,先讓我們做個initialState起始值
const initialState = {count: 87}
我們等等就根據這個state來進行變動,看是要加多少/減多少,再呈現在UI上。
接著創造actionTypes,我們要告訴React,之後只有哪些類型,可以對對應的state進行操作:
type ACTIONTYPES =
| { type: "increment"; payload: number }
| { type: "decremenet"; payload: number }
我們這邊用型別別名先宣告了兩個型別,特別的是型別內部的type
是明確的字串值,不是"increment"
就是"decremenet"
,payload則是之後我們想要讓他加多少/減多少的數量,型別為數字。
如果你不習慣這樣的宣告形式的話...只能請你習慣了,這是使用reducer必須的作法。
再來,我們終於要製作reducer了!
const countReducer = (state: typeof initialState, action: ACTIONTYPES) => {
switch (action.type) {
//要加多少
case "increment":
return {
...state,
count: state.count + action.payload
}
//要減多少
case "decremenet":
return {
...state,
count: state.count - action.payload
}
//如果不是正確的type,那就拋出錯誤吧
default:
throw new Error("noooooo")
}
}
我們宣告出一個函式:countReducer
,這函式有兩個參數:起始值(等等會放入initialState
)、action
物件,並根據action
物件內的type
來決定要對state執行的動作。
參數state
的型別,我們不另外宣告type或interface的話,就跟昨天一樣,用TypeScript的typeof
關鍵字,引用initialState
的型別{count: number}
,而action的型別,當然是剛剛宣告過的ACTIONTYPES
囉,編輯器就會很讚的跟我們說這個action.type有什麼能用了!
像我們在打case ""
的時候,編輯器就直接提供我們兩個選項"increment"
或"decremenet"
,完全不用另外翻找了呢。
扯了這麼多,剛剛都只是前置作業,我們還沒在元件內使用這些code呢,接下來請輸入這些code:
const ReducerComponent = () => {
//前置作業這麼長,只為下面這行
const [state, dispatch] = useReducer(countReducer, initialState)
return (
<>
<span>Current count is {state.count}</span>
<button
onClick={() => {
dispatch({ type: "increment", payload: 200 })
}}
>
Increment 200
</button>
<button
onClick={() => {
dispatch({ type: "decremenet", payload: 50 })
}}
>
Decrement 50
</button>
</>
)
}
//最後再自己渲染到App.tsx內就好了
我們終於用了useReducer了,他的參數有哪些呢?你可以在useReducer字樣上進行hover了解細節,簡單來說第一個就是reducer啦,處理後續state的變更邏輯,第二個參數則是初始狀態(你會發現我好像一直在講重複的事情,但這邊的確得這樣)。取到的值,跟useState有些類似,解構後的第一個值為state(一個有count的物件),第二個值是dispatch,用來發送action的函式。
渲染後,我們便能在螢幕上看到 Current count is 87
後面兩個按鈕,我們為他放入一個onClick的callback,分別發出(dispatch)一個action,dispatch的引數就是個action物件,所以當我們在輸入dispatch({action:"",payload:50})
的時候,編輯器又自動推薦我們可以選擇的action了,不要以為推薦這個沒什麼,當你的程式碼開始變多、需要一直跳來跳去時,這邊的提示就顯得相當關鍵。
最後你就會發現,我們能順利的透過按鈕更改state了!!當然這邊的state一點都不複雜啦,可是實際使用時,有reducer能先定義更改邏輯時,會發現程式更好控管了。
TypeScript用於useReducer
時能大大節省我們後續查找類型的時間,只要在reducer階段宣告好action的型別,以及initialState的型別,後續在進行每一步時,編輯器都會提示我們應該要寫些什麼,超方便的。
其實一開始在選這主題時,我是真的不覺得TypeScript好用,因為你要多寫太多的code了,實在很繁瑣,但隨著這幾天用React的各種hook當作範例後,就發現多寫的那些code,能讓開發者在後續開發上省下不少麻煩&時間,覺得花這些學習成本很值得!